/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2021. All rights reserved.
 * Description: hkrr main functions
 * Author: wangzhu
 * Create: 2021-07-05
 * Notes: it include following hkrr main functions :
 * 1. randomize .o objects, randomize the .o files according to start and step
 * 2. apply relocations for the total binary
 * 3. update kallsyms
 * 4. fix unwind sections to keep correct backtrace, or there is no backtrace
 * 5. apply relocations in .rel.dyn
 */

#ifdef CONFIG_RTOS_HAL_DEBUG_LL
#include <internal/rtos_debugll.h>
#endif
#include <linux/bits.h>

#include "hkrr_utils.h"

#ifdef CONFIG_ARM64

#define R_HKRR_ABS32			1
#define R_HKRR_ABS64			2
#define R_HKRR_PREL_32			3
#define R_HKRR_PREL_26			4
#define R_HKRR_PREL_LO19		5
#define R_HKRR_ADR_PREL_LO21		6
#define R_HKRR_ADR_PREL_PG_HI21		7
#define R_HKRR_LDST8_ABS_LO12_NC	8
#define R_HKRR_LDST16_ABS_LO12_NC	9
#define R_HKRR_LDST32_ABS_LO12_NC	10
#define R_HKRR_LDST64_ABS_LO12_NC	11
#define R_HKRR_ADR_GOT_PAGE		12
#define R_HKRR_LD64_GOT_LO12_NC		13
#define R_HKRR_PREL_64			14

#else

#define R_HKRR_ABS32		1
#define R_HKRR_PREL		2
#define R_HKRR_BREL		3
#define R_HKRR_GOT_BREL		4
#define R_HKRR_PREL_24		5
#define R_HKRR_PREL_31		6
#define R_HKRR_MOVW_ABS_NC	7
#define R_HKRR_MOVT_ABS		8
#define R_HKRR_MOVW_PREL_NC	9
#define R_HKRR_MOVT_PREL	10
#ifdef CONFIG_RTOS_HKRR_REDUCE_IMG_REL_SECTION
#define R_HKRR_PREL_VALUE	11
#endif

#endif

#define MAGIC_NUM		8
#define PAD_NUM			6
#define BYTES_OF_NUM(x)	(ALIGN((x), BITS_PER_BYTE) >> 3)

struct hkrr_ehdr {
	u8 e_ident[MAGIC_NUM]; /* ELF "magic number" */
	u8 e_obits;
	u8 e_tbits;
	u8 e_pad[PAD_NUM];
	u16 e_type;
	u16 e_machine;
	u32 e_version;
	uintptr_t e_entry; /* Entry point virtual address */
	uintptr_t e_phoff; /* Program header table file offset */
	uintptr_t e_shoff; /* Section header table file offset */
	u32 e_flags;
	u16 e_ehsize;
	u16 e_phentsize;
	u16 e_phnum;
	u16 e_shentsize;
	u16 e_shnum;
	u16 e_shstrndx;
} __packed;

struct hkrr_shdr {
	u32 sh_name; /* Section name, index in string tbl */
	u32 sh_type; /* Type of section */
	uintptr_t sh_flags; /* Miscellaneous section attributes */
	uintptr_t sh_addr; /* Section virtual addr at execution */
	uintptr_t sh_offset; /* Section file offset */
	uintptr_t sh_size; /* Size of section in bytes */
	u32 sh_link; /* Index of another section */
	u32 sh_info; /* Additional section information */
	uintptr_t sh_addralign; /* Section alignment */
	uintptr_t sh_entsize; /* Entry size if section holds table */
} __packed;

struct hkrr_rela {
	u32 r_offset; /* relative from _text */
	u8 r_info[0]; /* few bytes */
} __packed;

struct hkrr_objtab {
	u32 o_offset; /* relative from _text */
	u32 o_size;
	u32 o_align;
#ifdef CONFIG_RTOS_HKRR_DEBUG
	u32 o_name;
#endif
} __packed;

struct hkrr_sym {
	uintptr_t st_value;
	u16 st_name;
} __packed;

struct hkrr_parser {
	struct hkrr_ehdr *eh;
	struct hkrr_shdr *shs;
	char *shstrtab;
	struct hkrr_sym *syms;
	int syms_count;
	char *strtab;
#ifdef CONFIG_RTOS_HKRR_DEBUG
	char *objstrtab;
#endif
	struct hkrr_rela *relas;
	int relas_count;
	struct hkrr_objtab *objs;
	int objs_count;
	long *objtab;
	u8 *kas_owner;
	int kas_count;
	struct hkrr_kastab *kastab;
	u8 *kas_names;
	int kas_names_size;
	void *data_start;
	void *data_end;
	int data_size;
	uintptr_t text;
	uintptr_t text_end;
	uintptr_t text2;
	uintptr_t text2_end;
	long copy_offset;
	u64 seed;
};

struct hkrr_info {
	uintptr_t raddr;
	uintptr_t oraddr;
	uintptr_t saddr;
	uintptr_t osaddr;
	int rtype;
	long roff;
	long soff;
	unsigned int rowner;
	unsigned int sowner;
	int r;
};

#ifdef CONFIG_RTOS_EXTRACT_KALLSYMS
struct hkrr_ksyms_tab {
	int osize;
	long *objtab;
	u8 *kas_owner;
};

static struct hkrr_ksyms_tab kyms_tab;
#endif

/* two parameters to randomize the objects */
static unsigned int hkrr_start, hkrr_step;

static uintptr_t kaslroffset __initdata;
static uintptr_t text_text_size __initdata;
static uintptr_t kernel_offset __initdata;
static struct hkrr_shdr *__init get_section(struct hkrr_parser *elf,
					    const char *name);
static u64 __init get_int(u8 *p, int size);
#ifndef CONFIG_RTOS_HKRR_REDUCE_IMG_REL_SECTION
static unsigned  __init get_rowner(struct hkrr_parser *elf, u64 r_info);
static unsigned  __init get_sowner(struct hkrr_parser *elf, u64 r_info);
#endif

#define BITMASK(x) ((1UL << (x)) - 1)

#ifdef CONFIG_ARM64
#define HKRR_FAULT_BRK_IMM       0x100
#define AARCH64_BRK_INSN    0xd4200000
#define AARCH64_BRK_FAULT   (AARCH64_BRK_INSN | (HKRR_FAULT_BRK_IMM << 5))

#define ARM_INSN_LOOP	0x14000000
#define ADR_IMM_LO_SHIFT	29
#define ADR_IMM_LO_LEN		2
#define ADR_IMM_HI_SHIFT	5
#define ADR_IMM_HI_LEN		19

#undef PAGENO
#define PAGENO(x)	((x) & PAGE_MASK)

#else
#define ARM_INSN_LOOP	0xeafffffe
#endif

enum hkrr_reloc_type {
	RELOC_TYPE_ABS = 1,
	RELOC_TYPE_PLACE,
	RELOC_TYPE_PAGE,
};

enum hkrr_imm_type {
	RELOC_IMM_12,
	RELOC_IMM_16,
	RELOC_IMM_19,
	RELOC_IMM_24,
	RELOC_IMM_26,
	RELOC_IMM_ADR,
};

#ifdef CONFIG_CORTEX_A9
static int hkrr_printk_forbidden = 1;

int get_hkrr_printk_forbidden(void)
{
	return hkrr_printk_forbidden;
}
#endif

static unsigned long __init sign_extend(unsigned long value, int len)
{
	unsigned long sign_bit = 1UL << (len - 1);

	if (value & sign_bit)
		return value | ~(sign_bit - 1);
	return value;
}

#ifdef CONFIG_ARM64

static int __init hkrr_reloc_data(struct hkrr_info *info,
				  enum hkrr_reloc_type type,
				  int len, bool ovf)
{
	long sval;
	uintptr_t place = info->raddr;

	switch (len) {
	case 32:
		if (type == RELOC_TYPE_ABS)
			sval = *(s32 *)place + info->soff;
		else
			sval = *(s32 *)place + (info->soff - info->roff);

		*(s32 *)place = sval;

		if (ovf && (sval < S32_MIN || sval > U32_MAX))
			return -ERANGE;
		break;
	case 64:
		if (type == RELOC_TYPE_ABS)
			sval = *(s64 *)place + info->soff;
		else
			sval = *(s64 *)place + (info->soff - info->roff);

		*(s64 *)place = sval;

		break;
	default:
		hkrr_log_strnum("Invalid length for data relocation: ", len, 1);
	}
	return 0;
}

#else

static int __init hkrr_reloc_data(struct hkrr_info *info,
				  enum hkrr_reloc_type type,
				  int len, bool ovf)
{
	uintptr_t place = info->raddr;

	switch (type) {
	case RELOC_TYPE_ABS:
		/*
		 * roff and soff can be both non-zero
		 * add only sym offset
		 */
		*(u32 *)place += info->soff;
		break;
	case RELOC_TYPE_PLACE:
		if (len == 32) {
			/* add relative offset between sym and loc */
			*(u32 *)place += (info->soff - info->roff);
		} else if (len == 31) {
			u32 value = *(u32 *)place;

			value = sign_extend(value, len);
			/* add relative offset between sym and loc */
			value += (info->soff - info->roff);
			/* overflow should not exists on 31 bit data */
			*(u32 *)place = value & ~(1 << len);
		}
		break;
	default:
		hkrr_log_strnum("Invalid relocation type: ", type, 1);
	}
	/* 32/31 bits has not overflow problems */
	return 0;
}

#endif

static unsigned long __init decode_imm(u32 insn, int s1, int n1, int r1)
{
	unsigned long imm = ((unsigned long)insn >> s1) & BITMASK(n1);

	imm <<= r1;
	return sign_extend(imm, n1 + r1);
}

static u32 __init encode_imm(u32 insn, unsigned long imm,
			     int s1, int n1, int r1)
{
	/* value mask */
	u32 m1 = BITMASK(n1);
	/* remove unsignificant bits */
	imm = (imm >> r1) & m1;
	/* clear the immediate */
	insn &= ~(m1 << s1);
	/* set the immediate */
	insn |= imm << s1;
	return insn;
}

static unsigned long __init decode_imm2(u32 insn,
					int s1, int n1, int r1 /* lsb */,
					int s2, int n2, int r2)
{
	unsigned long imm1 = ((unsigned long)insn >> s1) & BITMASK(n1);
	unsigned long imm2 = ((unsigned long)insn >> s2) & BITMASK(n2);

	imm1 <<= r1;
	imm2 <<= r2;
#ifdef CONFIG_ARM64
	return sign_extend(imm1 | imm2, n1 + n2 + r1 /* lsb */);
#else
	/* arm32 does not have/need sign_extend for imm2 */
	return (imm1 | imm2);
#endif
}

static u32 __init encode_imm2(u32 insn, unsigned long imm,
			      int s1, int n1, int r1,
			      int s2, int n2, int r2)
{
	/* value mask */
	u32 m1 = BITMASK(n1);
	u32 m2 = BITMASK(n2);
	/* remove unsignificant bits */
	u32 imm1 = (imm >> r1) & m1;
	u32 imm2 = (imm >> r2) & m2;
	/* clear the immediate */
	insn &= ~((m1 << s1) | (m2 << s2));
	/* set the immediate */
	insn |= (imm1 << s1) | (imm2 << s2);
	return insn;
}

#ifdef CONFIG_ARM64

static int __init hkrr_reloc_imm(struct hkrr_info *info,
				 enum hkrr_reloc_type type,
				 int lsb, int len,
				 enum hkrr_imm_type imm_type, bool ovf)
{
	long imm;
	uintptr_t oraddr;
	u32 *place = (u32 *)info->raddr;
	u32 insn = le32_to_cpu(*place);

	if (insn == AARCH64_BRK_FAULT)
		return 0;

	switch (imm_type) {
	case RELOC_IMM_12:
		imm = decode_imm(insn, 10, len, lsb);
		imm += info->soff;
		insn = encode_imm(insn, imm, 10, len, lsb);
		break;
	case RELOC_IMM_19:
		imm = decode_imm(insn, 5, len, lsb);
		imm += (info->soff - info->roff);
		insn = encode_imm(insn, imm, 5, len, lsb);
		break;
	case RELOC_IMM_26:
		imm = decode_imm(insn, 0, len, lsb);
		imm += (info->soff - info->roff);
		insn = encode_imm(insn, imm, 0, len, lsb);
		break;
	case RELOC_IMM_ADR:
		if (type == RELOC_TYPE_PAGE && info->soff) {
			/*
			 * imm cannot be used because of lack
			 * of information. Use symbol address
			 * provide in relocation information
			 */
			imm = PAGENO(info->saddr) - PAGENO(info->raddr);
		} else {
			imm = decode_imm2(insn, ADR_IMM_LO_SHIFT,
					  ADR_IMM_LO_LEN, lsb,
					  ADR_IMM_HI_SHIFT, ADR_IMM_HI_LEN,
					  lsb + ADR_IMM_LO_LEN);
			if (type == RELOC_TYPE_PAGE) {
				/* soff == 0 */
				oraddr = info->raddr - info->roff;
				imm += PAGENO(oraddr) - PAGENO(info->raddr);
			} else {
				imm += info->soff - info->roff;
			}
		}
		insn = encode_imm2(insn, imm,
				   ADR_IMM_LO_SHIFT, ADR_IMM_LO_LEN, lsb,
				   ADR_IMM_HI_SHIFT, ADR_IMM_HI_LEN,
				   lsb + ADR_IMM_LO_LEN);
		break;
	default:
		hkrr_log_strnum("unknown immediate type ", imm_type, 1);
		insn = AARCH64_BRK_FAULT;
	}

	*place = cpu_to_le32(insn);

	if (!ovf)
		return 0;

	/* test for overflow */
	imm >>= lsb + len - 1;
	if (imm == 0 || imm == -1)
		return 0;
	return -ERANGE;
}

static int __init hkrr_do_reloc(struct hkrr_info *info)
{
	/*
	 * "ELF for the ARM 64-bit Architecture" specification defines
	 * quite many relocation codes, which may be used in various
	 * cases (kernel, library, executable). But when building a
	 * kernel, for now, only the following 16 relocations are used.
	 * R_AARCH64_ADR_GOT_PAGE and R_AARCH64_LD64_GOT_LO12_NC are
	 * only used by clang. With compiler changes, it could be
	 * possible that certain type of relocations will not be used or
	 * new type of relocations will be used instead. In such case,
	 * it is necessary to update the implementation.
	 */
	switch (info->rtype) {
	/* case R_AARCH64_ABS32: */
	case R_HKRR_ABS32:
		return hkrr_reloc_data(info, RELOC_TYPE_ABS, 32, true);
	/* case R_AARCH64_ABS64: */
	case R_HKRR_ABS64:
		return hkrr_reloc_data(info, RELOC_TYPE_ABS, 64, false);
	/* case R_AARCH64_PREL32: */
	case R_HKRR_PREL_32:
		return hkrr_reloc_data(info, RELOC_TYPE_PLACE, 32, true);
	/*
	 * case R_AARCH64_CONDBR19:
	 * case R_AARCH64_LD_PREL_LO19:
	 */
	case R_HKRR_PREL_LO19:
		return hkrr_reloc_imm(info, RELOC_TYPE_PLACE, 2, 19,
				      RELOC_IMM_19, true);
	/*
	 * case R_AARCH64_JUMP26:
	 * case R_AARCH64_CALL26:
	 */
	case R_HKRR_PREL_26:
		return hkrr_reloc_imm(info, RELOC_TYPE_PLACE, 2, 26,
				      RELOC_IMM_26, true);
	/* case R_AARCH64_ADR_PREL_LO21: */
	case R_HKRR_ADR_PREL_LO21:
		return hkrr_reloc_imm(info, RELOC_TYPE_PLACE, 0, 21,
				      RELOC_IMM_ADR, true);
	/* case R_AARCH64_ADR_PREL_PG_HI21: */
	case R_HKRR_ADR_PREL_PG_HI21:
		return hkrr_reloc_imm(info, RELOC_TYPE_PAGE, 12, 21,
				      RELOC_IMM_ADR, true);
	/*
	 * case R_AARCH64_ADD_ABS_LO12_NC:
	 * case R_AARCH64_LDST8_ABS_LO12_NC:
	 */
	case R_HKRR_LDST8_ABS_LO12_NC:
		return hkrr_reloc_imm(info, RELOC_TYPE_ABS, 0, 12,
				      RELOC_IMM_12, false);
	/* case R_AARCH64_LDST16_ABS_LO12_NC: */
	case R_HKRR_LDST16_ABS_LO12_NC:
		return hkrr_reloc_imm(info, RELOC_TYPE_ABS, 1, 11,
				      RELOC_IMM_12, false);
	/* case R_AARCH64_LDST32_ABS_LO12_NC: */
	case R_HKRR_LDST32_ABS_LO12_NC:
		return hkrr_reloc_imm(info, RELOC_TYPE_ABS, 2, 10,
				      RELOC_IMM_12, false);
	/* case R_AARCH64_LDST64_ABS_LO12_NC: */
	case R_HKRR_LDST64_ABS_LO12_NC:
		return hkrr_reloc_imm(info, RELOC_TYPE_ABS, 3, 9,
				      RELOC_IMM_12, false);
	/* case R_AARCH64_ADR_GOT_PAGE: */
	case R_HKRR_ADR_GOT_PAGE:
		return hkrr_reloc_imm(info, RELOC_TYPE_PAGE, 12, 21,
				      RELOC_IMM_ADR, true);
	default:
		hkrr_log_strnum("relocation addr: ", info->r, 1);
		hkrr_log_strnum("relocation type: ", info->rtype, 1);
		hkrr_log_strnum("oraddr: ", info->oraddr, 0);
		return -ERANGE;
	}

	return -ERANGE;
}

static inline bool __init hkrr_have_rvalue(struct hkrr_info info)
{
	return info.sowner && info.rtype == R_HKRR_ADR_PREL_PG_HI21;
}

#else

static int __init hkrr_reloc_imm(struct hkrr_info *info,
				 enum hkrr_reloc_type type,
				 int lsb, int len,
				 enum hkrr_imm_type imm_type, bool ovf)
{
	u32 imm;
	u32 *place = (u32 *)info->raddr;
	u32 insn = le32_to_cpu(*place);

	if (imm_type == RELOC_IMM_24) {
		imm = decode_imm(insn, 0, 24, 2);
		imm += (info->soff - info->roff);
		insn = encode_imm(insn, imm, 0, 24, 2);
	} else if (imm_type == RELOC_IMM_16) {
		if (lsb == 0) {
			imm = decode_imm2(insn, 0, 12, lsb, 16, 4, lsb + 12);
			imm += info->soff;
		} else {
			/* MOVT requires value */
			/* PREL requires non kaslr adjusted value */
			imm = (type == RELOC_TYPE_PLACE) ? info->osaddr : info->saddr;
		}
		if (type == RELOC_TYPE_PLACE)
			imm -= info->roff;
		insn = encode_imm2(insn, imm, 0, 12, lsb, 16, 4, lsb + 12);
	} else {
		hkrr_log_strnum("unknown immediate type ", imm_type, 1);
	}

	*place = cpu_to_le32(insn);

	return 0;
}

static int __init hkrr_do_reloc(struct hkrr_info *info)
{
	switch (info->rtype) {
	/*
	 * case R_ARM_RELATIVE:
	 * B(S) + A
	 * case R_ARM_ABS32:
	 * case R_ARM_TARGET1:
	 *  (S + A) | T
	 */
	case R_HKRR_ABS32:
		return hkrr_reloc_data(info, RELOC_TYPE_ABS, 32, false);
	/* case R_ARM_PREL31: */
	case R_HKRR_PREL_31:
		/*
		 * ((S + A) | T) – P
		 *  this is used if CONFIG_ARM_UNWIND is in use
		 */
		return hkrr_reloc_data(info, RELOC_TYPE_PLACE, 31, true);
	/* case R_ARM_REL32: */
#ifdef CONFIG_RTOS_HKRR_REDUCE_IMG_REL_SECTION
	case R_HKRR_PREL_VALUE:
#endif
	case R_HKRR_PREL:
		/* ((S + A) | T) – P */
		return hkrr_reloc_data(info, RELOC_TYPE_PLACE, 32, false);
	/*
	 * case R_ARM_BASE_PREL:
	 * R_ARM_GOTPC
	 */
	case R_HKRR_BREL:
		/* B(S) + A – P */
		if (info->soff)
			hkrr_log_str("R_ARM_BASE_PREL: sym offset is not zero\n");
		return hkrr_reloc_data(info, RELOC_TYPE_PLACE, 32, false);
	/*
	 * case R_ARM_PC24:
	 * case R_ARM_JUMP24:
	 * case R_ARM_CALL:
	 */
	case R_HKRR_PREL_24:
		/* ((S + A) | T) – P */
		return hkrr_reloc_imm(info, RELOC_TYPE_PLACE, 2, 24,
				      RELOC_IMM_24, true);
	/* case R_ARM_MOVW_ABS_NC: */
	case R_HKRR_MOVW_ABS_NC:
		/* (S + A) | T */
		return hkrr_reloc_imm(info, RELOC_TYPE_ABS, 0, 16,
				      RELOC_IMM_16, false);
	/* case R_ARM_MOVT_ABS: */
	case R_HKRR_MOVT_ABS:
		/* S + A */
		return hkrr_reloc_imm(info, RELOC_TYPE_ABS, 16, 16,
				      RELOC_IMM_16, false);
	/* case R_ARM_MOVW_PREL_NC: */
	case R_HKRR_MOVW_PREL_NC:
		/* ((S + A) | T) – P */
		return hkrr_reloc_imm(info, RELOC_TYPE_PLACE, 0, 16,
				      RELOC_IMM_16, false);
	/* case R_ARM_MOVT_PREL: */
	case R_HKRR_MOVT_PREL:
		/* S + A – P */
		return hkrr_reloc_imm(info, RELOC_TYPE_PLACE, 16, 16,
				      RELOC_IMM_16, false);
	default:
		hkrr_log_strnum("unhandled relocation type: ", info->rtype, 1);
		return -ERANGE;
	}
	return -ERANGE;
}

static inline bool __init hkrr_have_rvalue(struct hkrr_info info)
{
#ifdef CONFIG_RTOS_HKRR_REDUCE_IMG_REL_SECTION
	return info.rtype == R_HKRR_MOVT_ABS || info.rtype == R_HKRR_MOVT_PREL ||
			info.rtype == R_HKRR_PREL_VALUE;
#else
	return info.rtype == R_HKRR_MOVT_ABS || info.rtype == R_HKRR_MOVT_PREL;
#endif
}

#endif

static struct hkrr_shdr *__init get_section(struct hkrr_parser *elf,
					    const char *name)
{
	int i;
	struct hkrr_shdr *sh = NULL;

	for (i = 0; i < elf->eh->e_shnum; i++) {
		sh = &elf->shs[i];
		if (!strncmp(elf->shstrtab + sh->sh_name, name, strlen(name)))
			return sh;
	}

	return NULL;
}

static struct hkrr_objtab *__init get_object(struct hkrr_parser *elf,
					     unsigned int index)
{
	if (index < elf->objs_count)
		return &elf->objs[index];
	return NULL;
}

static struct hkrr_parser hkrr_img __initdata;

static inline uintptr_t __init get_o_offset(struct hkrr_objtab *obj)
{
	return kernel_offset + obj->o_offset + kaslroffset;
}

static inline uintptr_t __init get_r_offset(struct hkrr_rela *rela)
{
	return kernel_offset + rela->r_offset;
}

static u64 __init get_int(u8 *p, int size)
{
	u64 r_info;
#ifndef CONFIG_CPU_BIG_ENDIAN
	int shift;
#endif

	if (!p)
		return 0;
	r_info = *p++;
#ifdef CONFIG_CPU_BIG_ENDIAN
	for (; --size;) {
		r_info <<= 8;
		r_info |= *p++;
	}
#else
	shift = 8;
	for (; --size; shift += 8) {
		r_info |= (u64)(*p++) << shift;
	}
#endif
	return r_info;
}

#ifdef CONFIG_RTOS_HKRR_REDUCE_IMG_REL_SECTION
static unsigned  __init get_rtype(struct hkrr_parser *elf, u32 *r_offset)
{
	unsigned int r_type, high_bits;

	high_bits = (32 - elf->eh->e_tbits);
	r_type = (*r_offset) >> high_bits;
	/*
	 * packed_offset struct is:
	 *     r_type (4 bits) | r_offset (28 bits)
	 * in arm32, r_offset is always started with 0xc, so we extract r_type value
	 * and replaced with 0xc to recover actual r_offset value
	 */
	(*r_offset) = ((*r_offset) & ((1 << high_bits) - 1)) | (0xc << high_bits);
	return r_type;
}
#else
static unsigned  __init get_rtype(struct hkrr_parser *elf, u64 r_info)
{
	return (r_info >> (2 * elf->eh->e_obits)) &
		((1 << elf->eh->e_tbits) - 1);
}

static unsigned  __init get_rowner(struct hkrr_parser *elf, u64 r_info)
{
	return r_info & ((1 << elf->eh->e_obits) - 1);
}

static unsigned  __init get_sowner(struct hkrr_parser *elf, u64 r_info)
{
	return (r_info >> elf->eh->e_obits) & ((1 << elf->eh->e_obits) - 1);
}
#endif

static int __init elf_section_setup(struct hkrr_parser *elf)
{
	struct hkrr_shdr *sh = NULL;

	elf->eh = (struct hkrr_ehdr *)__hkrr_table_start;
	elf->shs = (struct hkrr_shdr *)(__hkrr_table_start + elf->eh->e_shoff);
	sh = &elf->shs[elf->eh->e_shstrndx];
	hkrr_log_strnum("shstrtab offset: 0x", (LLU)sh->sh_offset, 0);
	elf->shstrtab = (char *)(__hkrr_table_start + sh->sh_offset);

	kaslroffset = kaslr_offset();
#ifdef CONFIG_ARM64
	/* relative to _text */
	kernel_offset = (uintptr_t)_text - kaslroffset;
#else
	kernel_offset = 0;
#endif

#ifdef CONFIG_RTOS_HKRR_DEBUG
	sh = get_section(elf, ".objstrtab");
	if (!sh) {
		hkrr_log_str(".objstrtab not found\n");
		return HKRR_ERROR;
	}
	elf->objstrtab = (char *)(__hkrr_table_start + sh->sh_offset);
#endif

	sh = get_section(elf, ".objtab");
	if (!sh) {
		hkrr_log_str(".objtab not found\n");
		return HKRR_ERROR;
	}

	elf->objs_count = sh->sh_size / sh->sh_entsize;
	elf->objs = (struct hkrr_objtab *)(__hkrr_table_start + sh->sh_offset);
	elf->data_size += ALIGN(sizeof(elf->objtab[0]) * elf->objs_count, 16);

	sh = get_section(elf, ".rel");
	if (!sh) {
		hkrr_log_str(".rela.text not found\n");
		return HKRR_ERROR;
	}

	elf->relas_count = sh->sh_size / sh->sh_entsize;
	elf->relas = (struct hkrr_rela *)(__hkrr_table_start + sh->sh_offset);

	hkrr_log_strnum("Using bits per type: ", elf->eh->e_tbits, 1);
	hkrr_log_strnum("Using bits per object: ", elf->eh->e_obits, 1);

	return HKRR_OK;
}

static void __init show_elf_info(struct hkrr_parser *elf)
{
#ifdef CONFIG_RTOS_HKRR_DEBUG
	hkrr_log_strnum("hkrr total required mem size: ", (LLU)elf->data_size, 1);

	hkrr_log_strnum("objtab: count: ", elf->objs_count - 1, 1);
	hkrr_log_strnum("objtab start: 0x", (unsigned long)elf->objtab, 0);
	hkrr_log_strnum("objtab end: 0x", (unsigned long)&elf->objtab[elf->objs_count], 0);
	hkrr_log_strnum("objtab size: 0x", (int)(sizeof(elf->objtab[0]) * elf->objs_count), 0);
	hkrr_log_strnum("kastab count: ", elf->kas_count, 1);
	hkrr_log_strnum("kastab address: 0x", (unsigned long)elf->kastab, 0);
	hkrr_log_strnum("kas_names_size: ", elf->kas_names_size, 1);

	hkrr_log_strnum("__text_text_start: 0x", (unsigned long)__text_text_start, 0);
	hkrr_log_strnum("__text_text_end: 0x", (unsigned long)__text_text_end, 0);
	hkrr_log_strnum("__got_start: 0x", (unsigned long)__got_start, 0);
	hkrr_log_strnum("__got_end: 0x", (unsigned long)__got_end, 0);
	hkrr_log_strnum("_text_text size: ", text_text_size, 1);
	hkrr_log_strnum("elf->text: 0x", (LLU)elf->text, 0);
	hkrr_log_strnum("elf->text_end: 0x", (LLU)elf->text_end, 0);
	hkrr_log_strnum("elf->text size: ", (LLD)elf->text_end - elf->text, 1);
	hkrr_log_strnum("elf->text2: 0x: ", (LLU)elf->text2, 0);
	hkrr_log_strnum("elf->text2_end: 0x", (LLU)elf->text2_end, 0);
	hkrr_log_strnum("elf->text2 size: ", (LLD)(elf->text2_end - elf->text2), 1);

	hkrr_log_strnum("__hkrr_data use: ", (LLU)(elf->data_end - elf->data_start), 1);
#endif
}

static int __init elf_text_setup(struct hkrr_parser *elf)
{
#ifdef CONFIG_KALLSYMS
	struct hkrr_shdr *sh = NULL;

	sh = get_section(elf, ".kasownertab");
	if (!sh) {
		hkrr_log_str(".kasownertab not found\n");
		return HKRR_ERROR;
	}

	elf->kas_owner = (u8 *)(__hkrr_table_start + sh->sh_offset);
#ifndef CONFIG_RTOS_EXTRACT_KALLSYMS
	elf->kas_count = kallsyms_num_syms;
	elf->kas_names_size = (const u8 *)kallsyms_markers - kallsyms_names;

	elf->data_size += ALIGN(sizeof(elf->kastab[0]) * elf->kas_count, 16);
	elf->data_size += ALIGN(elf->kas_names_size, 16);
#else
	elf->kas_count = sh->sh_size / sh->sh_entsize;
#endif
#endif

	/*
	 * elf->data_size includes following objects:
	 * 1. objtab
	 * 2. kastab
	 * 3. kas_names
	 * elf->data starts from __hkrr_data_start, start of HKRR_DATA_DATA,
	 * due to that max size of above structure is 16, so it is aligned to
	 * 16 bytes.
	 */
	elf->data_start = (void *)__hkrr_data_start;

	elf->objtab = elf->data_start;
#if defined(CONFIG_KALLSYMS) && !defined(CONFIG_RTOS_EXTRACT_KALLSYMS)
	elf->kastab = PTR_ALIGN(
		(typeof(elf->kastab))&elf->objtab[elf->objs_count], 16);
	elf->kas_names = PTR_ALIGN(
		(typeof(elf->kas_names))&elf->kastab[elf->kas_count], 16);
	elf->data_end = &elf->kas_names[elf->kas_names_size];
#else
	elf->data_end = &elf->objtab[elf->objs_count];
#endif

	text_text_size = (int)(__text_text_end - __text_text_start);

#ifdef CONFIG_RTOS_HKRR_TEXT_COPY_BACK
	elf->text = (uintptr_t)__text_text_start;
	elf->text_end = (uintptr_t)__text_text_end2;
	elf->text2 = (uintptr_t)__hkrr_text_start;
	elf->text2_end = (uintptr_t)__hkrr_text_end;
	elf->copy_offset = elf->text2 - elf->text;
	__memcpy((void *)elf->text2, (void *)elf->text, text_text_size);
#else   /* ! TEXT_COPY_BACK */
	elf->text = (uintptr_t)__hkrr_text_start;
	elf->text_end = (uintptr_t)__hkrr_text_end;
	elf->text2 = (uintptr_t)__text_text_start;
	elf->text2_end = (uintptr_t)__text_text_end2;
	elf->copy_offset = 0;
#endif

	__memset(elf->data_start, 0, elf->data_end - elf->data_start);
	show_elf_info(elf);
	local_flush_tlb_all();

	return HKRR_OK;
}

static int __init move_object(struct hkrr_parser *elf, unsigned long *pdst, int i, int *psize)
{
	struct hkrr_objtab *obj = NULL;
	unsigned long o_offset;
	long src;
#ifndef CONFIG_RTOS_HKRR_TEXT_COPY_BACK
	unsigned int *inst = NULL;
#endif

	if (elf == NULL || pdst == NULL || psize == NULL) {
		hkrr_log_str("Error paramters\n");
		return HKRR_ERROR;
	}

	obj = get_object(elf, i);
	if (!obj) {
		hkrr_log_strnum("not found object: ", i, 1);
		return HKRR_ERROR;
	}

	if (!obj->o_size)
		return HKRR_OK;

	o_offset = get_o_offset(obj);
	src = o_offset + elf->copy_offset;
	*pdst = ALIGN(*pdst, obj->o_align);
	if (*pdst & 0x3) {
		hkrr_log_strnum("offset is not 4 aligned: 0x", (LLU)*pdst, 0);
		return HKRR_ERROR;
	}

	if (*pdst + obj->o_size >= elf->text_end) {
		hkrr_log_str("NO MORE SPACE, INCREASE CONFIG_RTOS_HKRR_EXTRA_TEXT_SIZE\n");
		return HKRR_ERROR;
	}

	__memcpy((void *)*pdst, (const void *)(src), obj->o_size);

	elf->objtab[i] = *pdst - o_offset;
	*pdst += obj->o_size;
	*psize += obj->o_size;
#ifndef CONFIG_RTOS_HKRR_TEXT_COPY_BACK
	/* instead of zeroing the memory it is more
	 * useful to initialize it to "jump to itself"
	 * instructions so that if due to error in
	 * relocation it jumps to old address, it can
	 * spin on the same place. It simplifies
	 * debugging a lot.
	 */
	inst = (void *)src;
	for (; (uintptr_t)inst < (src + obj->o_size); inst++)
		*inst = cpu_to_le32(ARM_INSN_LOOP);
#endif

	return HKRR_OK;
}

static int __init randomize_objects(struct hkrr_parser *elf)
{
	int i, j, step, start;
	int objs_size = 0;
	unsigned long saddr;
	bool overflow;
#ifdef CONFIG_RTOS_HKRR_DEBUG
	int new_text_size = 0;
	int count = 0;
	int psize = 0;
#endif
	/* Phase #1 - randomize and move objects */

	/*
	 * method of randomization is based on randomly choose 2 numbers:
	 * start and step
	 * 'start' is the first object from the object table
	 * 'step' is the offset to the next element in the object table
	 * cycle is rotating over object table until all objects are moved
	 */

	saddr = elf->text;
	/* improve method to initialize 'start' and 'step' */
	start = elf->seed & 0xff;
	step = ((elf->seed >> 8) & 0xff) + 1;
	hkrr_start = start;
	hkrr_step = step;

	/*
	 * start and step is printed only on DEBUG build otherwise it is
	 * an information leak would allow to compute new object location
	 */
	hkrr_log_strnum("START: ", start, 1);
	hkrr_log_strnum("STEP: ", step, 1);

	for (j = 0; j < step; j++) {
		overflow = false;
		for (i = start + j;; i += step) {
			if (i >= elf->objs_count) {
				i -= elf->objs_count;
				overflow = true;
			}
			if (overflow && i >= start)
				break;

			if (move_object(elf, &saddr, i, &objs_size))
				return HKRR_ERROR;
#ifdef CONFIG_RTOS_HKRR_DEBUG
			if (psize != objs_size)
				count++;

			psize = objs_size;
#endif
		}
	}

#ifdef CONFIG_RTOS_HKRR_DEBUG
	new_text_size = saddr - elf->text;
	hkrr_log_strnum("moved objects number: ", count, 1);
	hkrr_log_strnum("increased text size: 0x", new_text_size - text_text_size, 0);
	hkrr_log_strnum("alignment: ", new_text_size - objs_size, 1);
#endif

	return HKRR_OK;
}

void hkrr_get_parameters(unsigned int *start, unsigned int *step)
{
	*start = hkrr_start;
	*step = hkrr_step;
}

#ifdef CONFIG_KALLSYMS

static int kas_cmpfun(const void *_a, const void *_b)
{
	const struct hkrr_kastab *a = _a;
	const struct hkrr_kastab *b = _b;

	if (a->kas_offset < b->kas_offset)
		return -1;
	else if (a->kas_offset > b->kas_offset)
		return 1;
	return 0;
}

static void kas_swapfun(void *_a, void *_b, int size)
{
	struct hkrr_kastab *a = _a;
	struct hkrr_kastab *b = _b;
	struct hkrr_kastab tmp;

	tmp = *a;
	*a = *b;
	*b = tmp;
}

#ifndef CONFIG_RTOS_EXTRACT_KALLSYMS
static void __init update_kallsyms(struct hkrr_parser *elf)
{
	/* Phase #2 - calculate new kallsyms addresses
	 * kallsyms table is organized in a very specific way
	 * names are compressed
	 * kallsyms_names string tab contains indexes of name substrings
	 * in order to find a name of the symbol it is necessary to parse
	 * entire kallsyms_names to find out offset
	 * this code first stores symbol offset and its name offset in the
	 * additional table 'kastab', then sort it and then populate
	 * kallsyms_offsets kallsyms_names again
	 */
	int i;
	const char *name;
	int count;
	unsigned int kowner;
	long off;
	u8 *p = elf->kas_owner;
	struct hkrr_kastab *kas = NULL;
	int osize = BYTES_OF_NUM(elf->eh->e_obits);

	hkrr_log_strnum("kallsyms num: ", kallsyms_num_syms, 1);

	for (i = 0, count = 0, off = 0; i < elf->kas_count; i++, p += osize) {
		kowner = get_int(p, osize);
		kas = &elf->kastab[i];
		kas->kas_offset = kallsyms_offsets[i];
		kas->kas_name = off;
		off += kallsyms_names[off] + 1;
		if (elf->objtab[kowner]) {
			kas->kas_offset += elf->objtab[kowner];
			count++;
		}
	}

	hkrr_log_strnum("changed kas: ", count, 1);
	hkrr_log_strnum("stream size: ", (LLD)off, 1);
	hkrr_log_str("sorting kas...\n");

	sort(elf->kastab, elf->kas_count, sizeof(elf->kastab[0]), kas_cmpfun, kas_swapfun);
	hkrr_log_str("creating new compressed names stream\n");

	off = 0;
	for (i = 0; i < elf->kas_count; i++) {
		kas = &elf->kastab[i];
		/* assing new offset value */
		kallsyms_offsets[i] =
			kas->kas_offset;
		name = (const char *)&kallsyms_names[kas->kas_name];
		count = *name + 1;
		__memcpy(elf->kas_names + off, name, count);
		if ((i & 0xFF) == 0)
			kallsyms_markers[i >> 8] = off;
		off += count;
	}

	hkrr_log_strnum("new stream size: ", (LLD)off, 1);

	/* copy new stream over the old one */
	if (elf->kas_count > 0)
		__memcpy(kallsyms_names, elf->kas_names, off);
}
#else
static u64 get_u64(u8 *p, int size)
{
	u64 r_info;
	int shift;

	if (!p)
		return 0;
	r_info = *p++;
#ifdef CONFIG_CPU_BIG_ENDIAN
	for (; --size;) {
		r_info <<= 8;
		r_info |= *p++;
	}
#else
	shift = 8;
	for (; --size; shift += 8)
		r_info |= (u64)(*p++) << shift;
#endif
	return r_info;
}

static void __init record_ksymstab(struct hkrr_parser *elf)
{
	int objtab_size;
	int kasownertab_size;

	kyms_tab.osize = BYTES_OF_NUM(elf->eh->e_obits);
	objtab_size = sizeof(elf->objtab[0]) * elf->objs_count;
	kasownertab_size = elf->kas_count * kyms_tab.osize;

	if (objtab_size > (OBJTAB_SIZE << KB_SHIFT)) {
		hkrr_log_strnum("WARNING: objtab exceeds reserved space: ", objtab_size, 1);
		return;
	} else if (kasownertab_size > (KAS_OWNER_TAB_SIZE << KB_SHIFT)) {
		hkrr_log_strnum("WARNING: kasownertab exceeds reserved space: ", kasownertab_size, 1);
		return;
	}

	__memcpy(__hkrr_objtab_start, elf->objtab, objtab_size);
	kyms_tab.objtab = (long *)__hkrr_objtab_start;
	__memcpy(__hkrr_kasownertab_start, elf->kas_owner, kasownertab_size);
	kyms_tab.kas_owner = (u8 *)__hkrr_kasownertab_start;
}

void hkrr_kallsyms_data_setup(u8 *kas_names, struct hkrr_kastab *kas)
{
	const char *name;
	int i;
	int count;
	long off;
	unsigned long kowner;
	u8 *p = kyms_tab.kas_owner;
	int osize = kyms_tab.osize;

	if (kas_names == NULL || kas == NULL)
		return;

	/*
	 * We only need to update the symbol table when HKRR has finished randomizing
	 * the kernel address. After completing the address randomization, we will call
	 * record_ksymstab() to record the addresses of objtab and kas_owner. So there
	 * is no need to update the symbol table when objtab and kas_owner are NULL.
	 */
	if (!kyms_tab.objtab || !kyms_tab.kas_owner)
		return;

	for (i = 0, off = 0; i < kallsyms_num_syms; i++, p += osize) {
		kowner = get_u64(p, osize);
		kas[i].kas_offset = kallsyms_offsets[i];
		kas[i].kas_name = off;
		off += kallsyms_names[off] + 1;
		if (kyms_tab.objtab[kowner])
			kas[i].kas_offset += kyms_tab.objtab[kowner];
	}

	sort(kas, kallsyms_num_syms, sizeof(struct hkrr_kastab), kas_cmpfun, kas_swapfun);

	for (i = 0, off = 0; i < kallsyms_num_syms; i++) {
		kallsyms_offsets[i] = kas[i].kas_offset;
		name = (const char *)&kallsyms_names[kas[i].kas_name];
		count = *name + 1;
		__memcpy(kas_names + off, name, count);
		if ((i & 0xFF) == 0)
			kallsyms_markers[i >> 8] = off;
		off += count;
	}
	/* copy new stream over the old one */
	if (kallsyms_num_syms > 0)
		__memcpy(kallsyms_names, kas_names, off);
}
#endif /* CONFIG_RTOS_EXTRACT_KALLSYMS */
#endif /* CONFIG_KALLSYMS */

#ifdef CONFIG_ARM_UNWIND

static int __init idx_cmpfun(const void *_a, const void *_b)
{
	const struct unwind_idx2 *a = _a;
	const struct unwind_idx2 *b = _b;

	if (a->addr < b->addr)
		return -1;
	else if (a->addr > b->addr)
		return 1;
	return 0;
}

/* Convert a prel31 symbol to an absolute address */
#define prel31_to_addr(ptr)				\
({							\
	/* sign-extend to 32 bits */			\
	long offset = (((long)*(ptr)) << 1) >> 1;	\
	(unsigned long)(ptr) + offset;			\
})

/* Convert an absolute address to a prel31 symbol */
#define addr_to_prel31(ptr)				\
({							\
	long offset = *ptr - (unsigned long)ptr;	\
	(offset & ~(1 << 31));				\
})

static int __init insert_unwind_idx(struct hkrr_parser *elf, unsigned int pkowner,
		unsigned int kowner, struct unwind_idx *idx, struct unwind_idx2 *idx2)
{
	int i;
	struct hkrr_objtab *obj = NULL;
	uintptr_t o_offset;
	int count = 0;

	if (elf == NULL || idx == NULL || idx2 == NULL) {
		hkrr_log_str("Pointer is NULL");
		return 0;
	}

	/* need to insert skipped objects */
	for (i = pkowner + 1; i <= kowner; i++) {
		obj = get_object(elf, i);
		o_offset = get_o_offset(obj);
		if (i == kowner && idx2->addr == o_offset) {
			/* no need prev entry if new entry
			 * correspond to object address
			 */
			continue;
		}
		/* duplicate previous index entry */
		*idx2 = *(idx2 - 1);
		/* but set address to be object address */
		idx2->addr = o_offset + elf->objtab[i];
		idx2++;
		count++;
		idx2->addr = prel31_to_addr(&idx->addr_offset);
	}

	return count;
}

static int __init fix_unwind_idx(struct hkrr_parser *elf, struct hkrr_shdr *sh)
{
	int count2 = 0;
	struct unwind_idx *idx = NULL;
	struct unwind_idx2 *idx2 = NULL;
	u8 *p = NULL;
	int osize = BYTES_OF_NUM(elf->eh->e_obits);
	unsigned int kowner = 0, pkowner = 0;

	p = (u8 *)(__hkrr_table_start + sh->sh_offset);
	/* convert to absolute address */
	idx = (struct unwind_idx *)__start_unwind_idx;
	idx2 = (struct unwind_idx2 *)elf->text2;
	for (; idx < __stop_unwind_idx; idx++, p += osize) {
		idx2->addr = prel31_to_addr(&idx->addr_offset);
		if (kowner)
			pkowner = kowner;
		kowner = get_int(p, osize);
		if (!kowner && pkowner && idx2->addr < (uintptr_t)__text_text_end) {
			/* got unowned index -> alignment *fill*
			 * just clear address to sorted it away during sorting
			 * entry will be duplicated if needed with new address
			 * and original insn
			 */
			idx2->addr = PAGE_OFFSET;
		}
		if (kowner && kowner != pkowner) {
			int inum = insert_unwind_idx(elf, pkowner, kowner, idx, idx2);
			idx2 += inum;
			count2 += inum;
		}
		if (kowner && elf->objtab[kowner])
			idx2->addr += elf->objtab[kowner];
		if ((idx->insn & HIGH_BIT_MASK) == 0 && idx->insn != 1) {
			/* prel31 to the unwind table */
			idx2->insn = prel31_to_addr(&idx->insn);
			/* mark entry that insn is an address
			 * use low addr bit, because funcitions 3 bytes aligned
			 */
			if (idx2->addr & 0x03)
				return HKRR_ERROR;
			/* insn is an address */
			idx2->addr |= 1;
		} else {
			idx2->insn = idx->insn;
		}
		idx2++;
		count2++;
	}

	return count2;
}

static void __init hkrr_fix_unwind_idx(struct hkrr_parser *elf)
{
#ifdef CONFIG_RTOS_HKRR_DEBUG
	int count = __stop_unwind_idx - __start_unwind_idx;
#endif
	int count2 = 0;
	struct unwind_idx *idx = NULL;
	struct unwind_idx2 *idx2 = NULL;
	struct hkrr_shdr *sh = NULL;

	if (elf == NULL) {
		hkrr_log_str("elf is NULL\n");
		return;
	}

	sh = get_section(elf, ".unwindownertab");
	if (!sh) {
		hkrr_log_str(".unwindownertab not found\n");
		return;
	}

#ifdef CONFIG_RTOS_HKRR_DEBUG
	hkrr_log_strnum("sorting unwind_idx: ", count, 1);
	hkrr_log_strnum("updating unwind_idx number: ", count, 1);
#endif

	count2 = fix_unwind_idx(elf, sh);
	if (count2 == HKRR_ERROR) {
		hkrr_log_str("fix unwind idx table failed\n");
		return;
	}

	__stop_unwind_idx2 = __start_unwind_idx + count2;
	idx = (struct unwind_idx *)__start_unwind_idx;
	idx2 = (struct unwind_idx2 *)elf->text2;
	sort(idx2, count2, sizeof(struct unwind_idx2), idx_cmpfun, NULL);
	for (; idx < __stop_unwind_idx2; idx++, idx2++) {
		idx->insn = idx2->insn;
		if (idx2->addr & 1) {
			/* convert address to prel31 */
			idx2->addr &= ~1; /* clear mark */
			idx->insn = addr_to_prel31(&idx->insn);
		}
		idx->addr_offset = idx2->addr;
		idx->addr_offset = addr_to_prel31(&idx->addr_offset);
	}
}
#endif

static void __init do_reloc(struct hkrr_info *info)
{
	int err;

	if (info == NULL) {
		hkrr_log_str("info is NULL\n");
		return;
	}

	if (!info->roff && !info->soff) {
		hkrr_log_strnum("skip reloc: 0x", (LLU)info->oraddr, 0);
		return;
	}

	info->saddr = info->osaddr + info->soff + kaslroffset;
	info->raddr = info->oraddr + info->roff + kaslroffset;

	err = hkrr_do_reloc(info);
	if (err == -ERANGE) {
		hkrr_log_strnum("overflow in relocation ", info->r, 1);
		hkrr_log_strnum("type ", info->rtype, 1);
		hkrr_log_strnum("raddr: 0x", (LLU)info->oraddr, 0);
		hkrr_log_strnum("saddr: 0x", (LLU)info->osaddr, 0);
	}
}

/* get owner through owner's address */
static u32 __init get_owner_by_addr(struct hkrr_parser *elf, unsigned long addr)
{
	u32 i = 1;
	u32 j = elf->objs_count - 1;
	u32 mid;
	unsigned long o_offset;
	struct hkrr_objtab *obj = NULL;

	while (i <= j) {
		mid = (i + j) / 2;
		obj = get_object(elf, mid);
		if (obj == NULL)
			return 0;
		o_offset = get_o_offset(obj);
		if (addr >= o_offset && addr < o_offset + obj->o_size)
			return mid;
		else if (addr < o_offset)
			j = mid - 1;
		else
			i = mid + 1;
	}

	return 0;
}

static void __init update_got(struct hkrr_parser *elf)
{
	unsigned long offs;
	unsigned long *got_entry = (unsigned long *)__got_start;
	unsigned long got_size = (unsigned long)__got_end - (unsigned long)__got_start;
	u32 owner;
	got_size /= sizeof(unsigned long);
	for (offs = 0; offs < got_size; offs++) {
		hkrr_log_strnum("got entry: 0x", *(got_entry + offs), 0);
		owner = get_owner_by_addr(elf, *(got_entry + offs));
		if (!owner)
			continue;
		*(got_entry + offs) += elf->objtab[owner];
		hkrr_log_strnum("got entry new addr: 0x", *(got_entry + offs), 0);
	}
}

#ifdef CONFIG_RTOS_HKRR_REDUCE_IMG_REL_SECTION
static inline uintptr_t __init get_rela_addr_by_offset(u32 offset)
{
	return kernel_offset + offset + kaslroffset;
}

/*
 * in hkrr.py we let relas sorted as rowner is first priority, sowner is second
 * priority, increased. if rowner changed, need_update is set to 1, means
 * sowner need to be reseted to 0.
 */
static int __init get_owner_by_offset(struct hkrr_parser *elf, long offs, int *owner)
{
	int need_update = 0;

	if (get_rela_addr_by_offset(offs) < (unsigned long)__text_text_start ||
		get_rela_addr_by_offset(offs) > (unsigned long)__text_text_end) {
		*owner = 0;
	} else {
		while (*owner < elf->objs_count &&
				elf->objs[*owner].o_offset + elf->objs[*owner].o_size <= offs) {
			(*owner)++;
			need_update = 1;
		}
	}
	return need_update;
}

/*
 * get soff based on roff and rvalue.
 * this function is based on get_rela_value in hkrr.py
 */
static long __init get_soff_by_roff(struct hkrr_parser *elf, struct hkrr_info *info)
{
	long soff = 0;
	u32 *place = (u32 *)(info->oraddr + info->roff + kaslroffset);
	u32 insn = le32_to_cpu(*place);

	switch (info->rtype) {
	case R_HKRR_ABS32:
		soff = *place - kaslroffset;
		break;
	case R_HKRR_PREL:
	case R_HKRR_BREL:
		soff = 0;
		break;
	case R_HKRR_PREL_24:
		soff = info->oraddr + decode_imm(insn, 0, 24, 2) + 8;
		break;
	case R_HKRR_PREL_31:
		soff = info->oraddr + sign_extend(*place, 31);
		break;
	case R_HKRR_MOVW_ABS_NC:
	case R_HKRR_MOVT_ABS:
	case R_HKRR_PREL_VALUE:
		/* rvalue */
		soff = info->osaddr;
		break;
	case R_HKRR_MOVW_PREL_NC:
	case R_HKRR_MOVT_PREL:
		soff = info->oraddr + info->osaddr;
		soff += (info->rtype == R_HKRR_MOVW_PREL_NC) ? 0x10 : 0x1c;
		break;
	}

	return soff;
}
#endif

static void __init apply_relocations(struct hkrr_parser *elf)
{
	int r;
	int count = 0;
	int rvcount = 0;
	struct hkrr_rela *rela = NULL;
	struct hkrr_info info;
#ifdef CONFIG_RTOS_HKRR_REDUCE_IMG_REL_SECTION
	int need_update = 0;
	long soff;
	int rowner = 0, sowner = 0;
#else
	u64 r_info;
#endif
	u8 *p = (u8 *)elf->relas;
	/* r_info consists of type owner and rower, so it need 2 obits */
	int ri_size = BYTES_OF_NUM(elf->eh->e_tbits + 2 * elf->eh->e_obits);

	hkrr_log_str("start apply relocations\n");

	for (r = 0; r < elf->relas_count; r++) {
		rela = (struct hkrr_rela *)p;

		/* move to next entry */
#ifdef CONFIG_RTOS_HKRR_REDUCE_IMG_REL_SECTION
		p += sizeof(struct hkrr_rela);
#else
		p += sizeof(struct hkrr_rela) + ri_size;
#endif

#ifdef CONFIG_RTOS_HKRR_REDUCE_IMG_REL_SECTION
		/*
		 * unpack r_type,r_offset from packed_offset,
		 * make sure this function is called before getting
		 * msg form rela.
		 */
		info.rtype = get_rtype(elf, &(rela->r_offset));
#else
		r_info = get_int(rela->r_info, ri_size);
		info.rtype = get_rtype(elf, r_info);
#endif
		info.oraddr = info.raddr = get_r_offset(rela);
		/* record next value */
		info.osaddr = info.saddr = *(uintptr_t *)p;
#ifdef CONFIG_RTOS_HKRR_REDUCE_IMG_REL_SECTION
		need_update = get_owner_by_offset(elf, rela->r_offset, &rowner);
		info.rowner = rowner;
#else
		info.rowner = get_rowner(elf, r_info);
#endif
		info.roff = elf->objtab[info.rowner];
#ifdef CONFIG_RTOS_HKRR_REDUCE_IMG_REL_SECTION
		/* before call this func, make sure info.roff is initialized */
		soff = get_soff_by_roff(elf, &info);
		if (need_update)
			sowner = 0;
		need_update = get_owner_by_offset(elf, soff, &sowner);
		info.sowner = sowner;
#else
		info.sowner = get_sowner(elf, r_info);
#endif
		info.soff = elf->objtab[info.sowner];
		info.r = r;

		do_reloc(&info);

		if (hkrr_have_rvalue(info)) {
			/*
			 * consume symbol value
			 * corresponds to 'self.include_value' flag
			 * in hkrr.py
			 */
			r++;
			p += sizeof(struct hkrr_rela) + ri_size;
			/* to verify if hkrr.c and hkrr.py output matches */
			rvcount++;
		}

		count++;
	}

	hkrr_log_strnum("performed relocation number: ", count, 1);
	hkrr_log_strnum("with value entry number: ", rvcount, 1);
	update_got(elf);

	HKRR_FLUSH_CACHE_ALL();
}

static noinline void __init hkrr_print_symbols(void)
{
	hkrr_log_strnum("printk location: 0x", (unsigned long)printk, 0);
}

static void __init hkrr_utils_init(struct hkrr_parser *elf)
{
	hkrr_print_addrs();
	hkrr_print_symbols();
}

static void __init hkrr_utils_cleanup(struct hkrr_parser *elf)
{
	hkrr_print_symbols();
	hkrr_print_log();
}

asmlinkage __visible void __init hkrr_early_init(void)
{
	u64 *magic = (u64 *)__hkrr_table_start;
	struct hkrr_parser *elf = &hkrr_img;

#ifdef CONFIG_RTOS_HAL_DEBUG_LL
	debugll_str("[Phase 2] booting...");
#endif

	hkrr_utils_init(elf);

	elf->seed = hkrr_get_hkrr_seed();

	/*
	 * seed is printed only on DEBUG build otherwise it is information leak
	 * would allow to compute new object location
	 */
	hkrr_log_strnum("HKRR SEED: 0x", (LLU)elf->seed, 0);
	if (!elf->seed)
		goto out;

	hkrr_log_strnum("__hkrr_table_start: 0x", (unsigned long)__hkrr_table_start, 0);
	hkrr_log_strnum("__hkrr_table_end: 0x", (unsigned long)__hkrr_table_end, 0);
	hkrr_log_strnum("__hkrr_table size: ", __hkrr_table_end - __hkrr_table_start, 1);

	if (*magic != ELF_MAGIC)
		goto out;

	hkrr_log_strnum("MAGIC: 0x", (uintptr_t)magic, 0);
	hkrr_log_str("MAGIC: ");
	hkrr_log_str((char *)magic);

	if (elf_section_setup(elf))
		goto out;

	if (elf_text_setup(elf))
		goto out;

	if (randomize_objects(elf))
		goto out;

	apply_relocations(elf);

#ifdef CONFIG_KALLSYMS
#ifdef CONFIG_RTOS_EXTRACT_KALLSYMS
	record_ksymstab(elf);
#else
	update_kallsyms(elf);
#endif
#endif
#ifdef CONFIG_ARM_UNWIND
	hkrr_fix_unwind_idx(elf);
#endif

out:
#ifdef CONFIG_CORTEX_A9
	hkrr_printk_forbidden = 0;
#endif
	hkrr_utils_cleanup(elf);
#ifdef CONFIG_RTOS_HAL_DEBUG_LL
	debugll_str("done\n");
#endif
	return;
}

void hkrr_debug_info(void)
{
	unsigned int start, step;

	hkrr_get_parameters(&start, &step);
	pr_info("HKRR parameters: START: %u, STEP: %u\n", start, step);
}
#ifdef CONFIG_RTOS_KSNAPSHOT
EXPORT_SYMBOL_NS(hkrr_debug_info, RTOS_DEBUG_KSNAPSHOT);
#endif

static int hkrr_reboot_print(struct notifier_block *this,
				unsigned long event, void *ptr)
{
	unsigned int start, step;

	hkrr_get_parameters(&start, &step);
	pr_info("HKRR parameters: START: %u, STEP: %u\n", start, step);
	return 0;
}

static struct notifier_block hkrr_reboot_notifier = {
	.notifier_call = hkrr_reboot_print,
};

static int hkrr_reboot_init(void)
{
	return register_reboot_notifier(&hkrr_reboot_notifier);
}

late_initcall(hkrr_reboot_init);
